﻿/*
   Copyright 2012 University of Southampton

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using uk.ac.soton.ses.DAL;
using Microsoft.SharePoint;

namespace uk.ac.soton.ses.UserControls
{
    public partial class ExperimentUsers : UserControl
    {
        private int _experimentID = -1;
        private bool _initialised = false;
        MaterialsMetadataContext dbcontext = null;

        /// <summary>
        /// A private class for populating the data for the gridview
        /// </summary>
        private class View_Users_DataItem
        {
            public Int32 ACEID { get; set; }
            public Int32 DBUserID { get; set; }
            public string Username { get; set; }
            public string Name { get; set; }
            public string Email { get; set; }
            public bool WriteAccessGranted { get; set; }
        }

        #region Page life cycle methods

        /// <summary>
        /// OnInit in ASP.NET page life cycle overridden. We configure events here.
        /// </summary>
        /// <param name="e">Event arguments received by events</param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            string idString = Page.Request.QueryString["ID"];
            if (int.TryParse(idString, out this._experimentID) && this._experimentID > 0)
            {
                this._initialised = true;
                this.btn_GrantUser.Click += new EventHandler(btn_GrantUser_Click);
                this.View_Users.RowDeleting += new GridViewDeleteEventHandler(View_Users_RowDeleting);
                this.View_Users.RowDataBound += new GridViewRowEventHandler(View_Users_RowDataBound);
                this.dbcontext = HttpContext.Current.GetMaterialsDBContext();
            }
        }

        /// <summary>
        /// Page_Load in ASP.NET page life cycle. We databind controls here.
        /// </summary>
        /// <param name="sender">Usual sender object received by events</param>
        /// <param name="e">Event arguments received by events</param>
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack && this._initialised)
            {
                this.DataBindAllControls();
            }
        }

        #endregion

        #region Data binding methods

        /// <summary>
        /// Parent data bind method that calls data binding on all required controls
        /// </summary>
        private void DataBindAllControls()
        {
            //Bind the user access grid view
            this.DataBindUsersGrid();
        }

        /// <summary>
        /// Data bind the user access grid
        /// </summary>
        private void DataBindUsersGrid()
        {
            //Load the data and generate an IQueryable for binding
            IQueryable<View_Users_DataItem> users =
                dbcontext.UserAccesses.Where(ace => ace.Experiment.ID == this._experimentID)
                .Select(ace => new View_Users_DataItem 
                { 
                    ACEID = ace.ID, 
                    DBUserID = ace.User.ID,
                    Name = ace.User.Name,
                    Username = ace.User.Username,
                    Email = ace.User.Email,
                    WriteAccessGranted = ace.WriteAccessGranted
                });
            this.View_Users.DataSource = users;
            this.View_Users.DataBind();
        }

        #endregion

        #region Private helper methods

        /// <summary>
        /// Checks to ensure we have permissions to modify the experiment's permissions
        /// </summary>
        private void CheckPermissions()
        {
            //Use the current user
            WindowsIdentity winIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
            Experiment experiment = dbcontext.GetExperimentByID(this._experimentID);
            if (!experiment.IsOwner(winIdentity, dbcontext))
            {
                throw new ExperimentAccessDeniedException("Not owner");
            }
        }

        #endregion

        #region View_Users Events

        /// <summary>
        /// View_Users generated event (fired when someone clicks revoke). Used to remove a user's access from an experiment
        /// </summary>
        /// <param name="sender">The control that generated the event</param>
        /// <param name="e">The event arguments</param>
        private void View_Users_RowDeleting(object sender, GridViewDeleteEventArgs e)
        {
            try
            {
                //Check we have permissions to do this
                this.CheckPermissions();

                //Remove the user from the experiment
                dbcontext.DeleteUserAccessToExperiment((int)View_Users.DataKeys[e.RowIndex]["DBUserID"], this._experimentID);
                dbcontext.SaveChanges();

                //Rebind the data
                this.DataBindUsersGrid();
            }
            catch (ExperimentAccessDeniedException)
            {
                this.lbl_Error.Text = "Not owner - only the owner can do this";
            }
        }

        /// <summary>
        /// View_Users generated event (fired for each row), used to set the read/write radio button's state
        /// </summary>
        private void View_Users_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            //Skip header row
            if (e.Row.RowIndex > -1)
            {
                //Get data
                View_Users_DataItem dataitem = e.Row.DataItem as View_Users_DataItem;

                //Set the read/write radio button
                RadioButtonList list = e.Row.FindControl("List_WriteAccess") as RadioButtonList;
                list.SelectedIndex = dataitem.WriteAccessGranted ? 1 : 0;
            }
        }

        /// <summary>
        /// View_Users generated event (fired when a radio button changes).
        /// </summary>
        /// <param name="sender">The control that generated the event</param>
        /// <param name="e">The event arguments</param>
        protected void View_User_WriteAccessChanged(object sender, EventArgs e)
        {
            //Find out which row our control is on
            int rowindex = ((GridViewRow)((RadioButtonList)sender).NamingContainer).RowIndex;

            //Find the DBUserID key field using the rowindex
            int dbuserid = (int)View_Users.DataKeys[rowindex]["DBUserID"];

            //We will toggle the permissions, so read access becomes write and vice versa
            dbcontext.ToggleWriteAccessToExperiment(dbuserid, this._experimentID);
            dbcontext.SaveChanges();

            //Rebind the grid
            this.DataBindUsersGrid();
        }

        #endregion

        #region Button Events

        /// <summary>
        /// Handles the click event, generated when the Grant button is clicked
        /// </summary>
        /// <param name="sender">The control that generated the event</param>
        /// <param name="e">The event arguments</param>
        private void btn_GrantUser_Click(object sender, EventArgs e)
        {
            //Find out which user was specified
            string newUser = this.TextBox_NewUser.Text;
            if (newUser.Length > 0)
            {
                try
                {
                    //Check the user is permitted to do this!
                    this.CheckPermissions();

                    //Look up the user (which will generate an exception if it doesn't exist)
                    NTAccount acc = new NTAccount(newUser);
                    SecurityIdentifier si = acc.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;

                    //A WMI Query might be required which needs elevated priveleges
                    SPSecurity.RunWithElevatedPrivileges(delegate()
                    {
                        //Modify the database to grant the permission
                        dbcontext.GrantUserToExperiment(acc.ToString(), this._experimentID, false);
                        dbcontext.SaveChanges();
                    });

                    //Rebind the grid
                    this.DataBindUsersGrid();
                }
                catch (IdentityNotMappedException)
                {
                    this.lbl_Error.Text = "Could not find user " + newUser;
                }
                catch (ExperimentAccessDeniedException)
                {
                    this.lbl_Error.Text = "Not owner";
                }
                catch (Exception ex)
                {
                    this.lbl_Error.Text = "Exception: " + ex.Message;
                }
            }
        }

        #endregion
    }
}
